package connection;

import java.awt.Image;
import java.awt.Toolkit;
import java.io.IOException;

import pteidlib.PTEID_Certif;
import pteidlib.PTEID_ID;
import pteidlib.PteidException;
import pteidlib.pteid;
import sun.security.pkcs11.wrapper.CK_ATTRIBUTE;
import sun.security.pkcs11.wrapper.CK_MECHANISM;
import sun.security.pkcs11.wrapper.PKCS11;
import sun.security.pkcs11.wrapper.PKCS11Constants;
import sun.security.pkcs11.wrapper.PKCS11Exception;
import utils.Base64Processing;
import connection.exception.CardErrorException;

public class CitizenCardInterface {

	public static byte[] getCertificate() throws CardErrorException{
		try {
			String osName = System.getProperty("os.name");
			if (osName.equals("Linux")) {
				System.load("/usr/local/lib/pteid_jni/libpteidlibj.so");
			} else {
				System.loadLibrary("pteidlibj");
			}
			
			pteid.Init("");

			int cardtype = pteid.GetCardType();

			switch (cardtype)
			{
			case pteid.CARD_TYPE_IAS07:
				System.out.println("IAS 0.7 card detected. \n");
				break;
			case pteid.CARD_TYPE_IAS101:
				System.out.println("IAS 1.0.1 card detected. \n");
				break;
			case pteid.CARD_TYPE_ERR:
				System.out.println("Unable to get the card type. \n");
				break;
			default:
				System.out.println("Unknown card type. \n");
			}

			PTEID_Certif[] certificates =  pteid.GetCertificates();


			for(PTEID_Certif certificate : certificates){

				System.out.println(certificate.certifLabel);

				// Change Comparison to CITIZEN SIGNATURE CERTIFICATE to use qualified signature certificate
				if(certificate.certifLabel.compareTo("CITIZEN AUTHENTICATION CERTIFICATE")==0){
					return certificate.certif;
				}
			}
		}catch (PteidException e) {
			throw new CardErrorException();
		}
		return null;
	}

	public static String getSubjectName() throws CardErrorException{
		try {
			String osName = System.getProperty("os.name");
			if (osName.equals("Linux")) {
				System.load("/usr/local/lib/pteid_jni/libpteidlibj.so");
			} else {
				System.loadLibrary("pteidlibj");
			}
			
			pteid.Init("");
			pteid.SetSODChecking(false);

			PTEID_ID subjectId = pteid.GetID();

			return subjectId.firstname+" "+subjectId.name;
		} catch (PteidException e) {
			throw new CardErrorException();
		}
	}

	/**
	 * Returns the first and last names from the card user.
	 * 
	 * @return The String[] containing the first and last names of its bearer.
	 * @throws CardErrorException
	 */
	public static String[] getFirstLastName() throws CardErrorException {
		try {
			String osName = System.getProperty("os.name");
			if (osName.equals("Linux")) {
				System.load("/usr/local/lib/pteid_jni/libpteidlibj.so");
			} else {
				System.loadLibrary("pteidlibj");
			}
			
			pteid.Init("");
			pteid.SetSODChecking(false);

			PTEID_ID subjectId = pteid.GetID();

			String[] res = new String[2];

			String[] tmp = subjectId.name.split(" ");

			res[0] = subjectId.firstname.split(" ")[0];
			res[1] = tmp[tmp.length - 1];

			return res;
		} catch (PteidException e) {
			throw new CardErrorException();
		}        
	}

	public static byte[] SignData(String dataToSign) throws CardErrorException {

		PKCS11.loadNative();
		
		try {
			String osName = System.getProperty("os.name");
			if (osName.equals("Linux")) {
				System.load("/usr/local/lib/pteid_jni/libpteidlibj.so");
			} else {
				System.loadLibrary("pteidlibj");
			}
		} catch (UnsatisfiedLinkError e) {
			System.err.println("Native code library failed to load.\n" + e);
			System.exit(1);
		}

		try {
			String libLocation;
			String osName = System.getProperty("os.name");
			if (osName.equals("Linux")) {
				libLocation = "/usr/local/lib/libpteidpkcs11.so";
			} else {
				libLocation = "pteidpkcs11.dll";
			}

			PKCS11 pkcs = PKCS11.getInstance(libLocation, "C_GetFunctionList", null, false);

			long hsession = pkcs.C_OpenSession(0,PKCS11Constants.CKF_SERIAL_SESSION, null, null);

			// Find the signature private key
			CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[1];
			attributes[0] = new CK_ATTRIBUTE();
			attributes[0].type = PKCS11Constants.CKA_CLASS;
			attributes[0].pValue = new Long(PKCS11Constants.CKO_PRIVATE_KEY);

			pkcs.C_FindObjectsInit(hsession, attributes);

			long[] keyHandles = pkcs.C_FindObjects(hsession, 1);
			long signatureKey = keyHandles[0];

			pkcs.C_FindObjectsFinal(hsession);

			// Initialize the signature
			CK_MECHANISM mechanism = new CK_MECHANISM();
			mechanism.mechanism = PKCS11Constants.CKM_SHA1_RSA_PKCS;
			mechanism.pParameter = null;
			pkcs.C_SignInit(hsession, mechanism, signatureKey);

			// Sign the data
			byte[] signedBytes = pkcs.C_Sign(hsession, dataToSign.getBytes());

			String signedString = Base64Processing.encodeData(signedBytes);

			System.out.println("Signed challenge: \n" + signedString);

			return signedBytes;

		} catch (IOException e) {
			System.out.println("An exception has occured while trying to contact card!! \n");
			throw new CardErrorException();
		} catch (PKCS11Exception e) {
			System.out.println("An exception has occured in PKCS11 interface!! \n");
			return null;
		}
	}

	public static Image getCardPicture() throws CardErrorException{
		try {
			String osName = System.getProperty("os.name");
			if (osName.equals("Linux")) {
				System.load("/usr/local/lib/pteid_jni/libpteidlibj.so");
			} else {
				System.loadLibrary("pteidlibj");
			}
			
			pteid.Init("");
			pteid.SetSODChecking(false);

			return Toolkit.getDefaultToolkit().createImage(pteid.GetPic().picture);
		} catch (PteidException e) {
			throw new CardErrorException();
		}
	}

	public static String getUserCity() throws CardErrorException{
		try {
			String osName = System.getProperty("os.name");
			if (osName.equals("Linux")) {
				System.load("/usr/local/lib/pteid_jni/libpteidlibj.so");
			} else {
				System.loadLibrary("pteidlibj");
			}
			
			pteid.Init("");
			pteid.SetSODChecking(false);

			return pteid.GetAddr().municipality;
		} catch (PteidException e) {
			throw new CardErrorException();
		}
	}
}
